/**
 * 
 */
package wong.board.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import wong.board.client.resources.BoardBundle.BoardCss;
import wong.board.client.ui.BookmarksPresenter;
import wong.board.client.ui.DevelopmentPresenter;
import wong.board.client.ui.NotesPresenter;
import wong.board.client.ui.Presenter;
import wong.board.client.ui.SearchPresenter;
import wong.board.client.ui.ShowcasesPresenter;
import wong.board.client.ui.TitlePresenter;
import wong.board.client.ui.ValidationPresenter;
import wong.board.client.util.DebugTool;
import wong.board.client.util.JSONTool;
import wong.board.client.widgets.Content;
import wong.board.client.widgets.ContentPanel;
import wong.board.shared.ContentKeys;

import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * @author wong
 * 
 */
public class BoardContent {
	private static Logger logger = Logger.getLogger(BoardContent.class.getName());
	protected static final BoardCss boardCss = Board.getBundle().boardCss();
	public static final String BODY_ID = "main";
	//
	private RootPanel body;
	private Content headerContent;
	// private HeaderPresenter headerPresenter;
	private FlowPanel placeholder;
	private ValidationPresenter validationPresenter;
	private SearchPresenter searchPresenter;
	private TitlePresenter titlePresenter;
	private DevelopmentPresenter developmentPresenter;
	private BookmarksPresenter bookmarksPresenter;
	private ShowcasesPresenter showcasesPresenter;
	private NotesPresenter notesPresenter;
	private HandlerRegistration windowResizeRegistration;
	//
	private Map<ContentKeys, List<Presenter<?, ?>>> presentersLayout;

	/**
	 * 
	 */
	public BoardContent() {
	}

	public void setup() {
		logger.config("Setting up...");
		body = RootPanel.get(BODY_ID);
		if (body != null) {
			body.addStyleName(Board.getBundle().boardCss().board());
			//
			headerContent = new ContentPanel();
			headerContent.asWidget().setStyleName(boardCss.header());
			// headerPresenter = new HeaderPresenter();
			placeholder = new FlowPanel();
			placeholder.addStyleName(Board.getBundle().boardCss().placeholder());
			body.add(placeholder);
			body.add(headerContent);
			// body.add(headerPresenter.getView());
			WindowHandler handler = new WindowHandler();
			windowResizeRegistration = Window.addResizeHandler(handler);
			presentersLayout = new HashMap<ContentKeys, List<Presenter<?, ?>>>();
			logger.config("... the app has been set up.");
		} else {
			throw new IllegalStateException("no body element with id '" + BODY_ID + "' found.");
		}
	}

	public void applyContent(JSONObject content) {
		logger.config("Applying content.");

		Set<String> keySet = content.keySet();
		for (String key : keySet) {
			try {
				ContentKeys sectionKey = ContentKeys.valueOf(key);
				JSONObject sectionObject = JSONTool.isObject(content, key);

				// handle header or body sections
				switch (sectionKey) {
				case Header:
				case Body:
					updateSubSections(sectionObject, sectionKey);
					break;
				default:
					logger.warning("Section '" + sectionKey + "' will not be processed on the top level.");
					break;
				}
			} catch (Exception e) {
				Board.getErrorHandler().handleError(
						"Error handling section '" + ContentKeys.Body.toString() + "'", e);
			}
		}
	}

	public void attachViews() {
		Set<ContentKeys> keySet = presentersLayout.keySet();
		for (ContentKeys contentKeys : keySet) {
			List<Presenter<?, ?>> presenters = presentersLayout.get(contentKeys);
			for (Presenter<?, ?> presenter : presenters) {
				addPresenter(presenter, contentKeys);
			}
		}
		resizePlaceholder();
	}

	private void addPresenter(Presenter<?, ?> presenter, ContentKeys sectionKey) {
		switch (sectionKey) {
		case Header:
			headerContent.add(presenter.getView());
			break;
		case Body:
			addToBody(presenter.getView());
			break;
		default:
			logger.warning("unknown section: '" + sectionKey + "'");
			break;
		}
	}

	private void addToBody(IsWidget widget) {
		if (widget != null) {
			int i = body.getWidgetIndex(headerContent);
			body.insert(widget, i);
		}
	}

	public void flush() {
		if (windowResizeRegistration != null) {
			windowResizeRegistration.removeHandler();
			windowResizeRegistration = null;
		}

		flushPresenters();

		placeholder.asWidget().removeFromParent();
		placeholder = null;
	}

	protected void updateSubSections(JSONObject sectionObject, ContentKeys parentSectionKey) {
		Set<String> presenterNames = sectionObject.keySet();
		int index = 0;
		for (String presenterName : presenterNames) {
			Presenter<Presenter.View, JSONValue> presenter = getPresenter(presenterName);
			if (presenter != null) {
				putPresenter(parentSectionKey, presenter, index);
				index++;
				try {
					JSONValue value = sectionObject.get(presenterName);
					if (value != null) {
						presenter.updateView(value);
					} else {
						logger.warning("the value for presenter name '" + presenterName + "' in section "
								+ parentSectionKey.toString());
					}
				} catch (Exception e) {
					Board.getErrorHandler().handleError(
							"error while handling presenter name '" + presenterName + "'' in section "
									+ parentSectionKey.toString(), e);
				}
			} else {
				logger.warning("no presenter for presenter name '" + presenterName + "' in section "
						+ parentSectionKey.toString());
			}
		}

	}

	@SuppressWarnings("unchecked")
	private <View extends Presenter.View, Value> Presenter<View, Value> getPresenter(String key) {
		// TODO get rid of this ugly stuff using deferred binding.
		if (key.equals(ContentKeys.Title.toString())) {
			if (titlePresenter == null) {
				titlePresenter = new TitlePresenter();
			}
			return (Presenter<View, Value>) titlePresenter;
		} else if (key.equals(ContentKeys.Validation.toString())) {
			if (validationPresenter == null) {
				validationPresenter = new ValidationPresenter();
			}
			return (Presenter<View, Value>) validationPresenter;
		} else if (key.equals(ContentKeys.Search.toString())) {
			if (searchPresenter == null) {
				searchPresenter = new SearchPresenter();
			}
			return (Presenter<View, Value>) searchPresenter;
		} else if (key.equals(ContentKeys.Bookmarks.toString())) {
			if (bookmarksPresenter == null) {
				bookmarksPresenter = new BookmarksPresenter();
			}
			return (Presenter<View, Value>) bookmarksPresenter;
		} else if (key.equals(ContentKeys.Development.toString())) {
			if (developmentPresenter == null) {
				developmentPresenter = new DevelopmentPresenter();
			}
			return (Presenter<View, Value>) developmentPresenter;
		} else if (key.equals(ContentKeys.Showcases.toString())) {
			if (showcasesPresenter == null) {
				showcasesPresenter = new ShowcasesPresenter();
			}
			return (Presenter<View, Value>) showcasesPresenter;
		} else if (key.equals(ContentKeys.Notes.toString())) {
			if (notesPresenter == null) {
				notesPresenter = new NotesPresenter();
			}
			return (Presenter<View, Value>) notesPresenter;
		}
		return null;
	}

	private void putPresenter(ContentKeys section, Presenter<?, ?> presenter, int index) {
		List<Presenter<?, ?>> list = presentersLayout.get(section);
		if (list == null) {
			list = new ArrayList<Presenter<?, ?>>();
			presentersLayout.put(section, list);
		}
		if (isAvailable(presenter)) {
			removePresenter(presenter);
		}
		list.add(index, presenter);
	}

	private void removePresenter(Presenter<?, ?> presenter) {
		Set<ContentKeys> sectionKeys = presentersLayout.keySet();
		for (ContentKeys sectionKey : sectionKeys) {
			List<Presenter<?, ?>> list = presentersLayout.get(sectionKey);
			if (list.contains(presenter)) {
				list.remove(presenter);
			}
		}
	}

	private boolean isAvailable(Presenter<?, ?> presenter) {
		Set<ContentKeys> sectionKeys = presentersLayout.keySet();
		for (ContentKeys sectionKey : sectionKeys) {
			List<Presenter<?, ?>> list = presentersLayout.get(sectionKey);
			if (list.contains(presenter)) {
				return true;
			}
		}
		return false;
	}

	protected void resizePlaceholder() {
		int height = headerContent.asWidget().getOffsetHeight();
		placeholder.setHeight(height + "px");
	}

	protected void flushPresenters() {
		flushPresenter(validationPresenter);
		validationPresenter = null;
		flushPresenter(searchPresenter);
		searchPresenter = null;
		// flushPresenter(headerPresenter);
		// headerPresenter = null;
		flushPresenter(titlePresenter);
		titlePresenter = null;
		flushPresenter(developmentPresenter);
		developmentPresenter = null;
		flushPresenter(bookmarksPresenter);
		bookmarksPresenter = null;
		flushPresenter(showcasesPresenter);
		showcasesPresenter = null;
		flushPresenter(notesPresenter);
		notesPresenter = null;
	}

	private void flushPresenter(Presenter<?, ?> presenter) {
		if (presenter == null) {
			return;
		}
		try {
			presenter.getView().asWidget().removeFromParent();
			presenter.flush();
		} catch (Exception ex) {
			DebugTool.showError("cannot flush presenter", ex);
		}
	}

	/*
	 * 
	 */

	private class WindowHandler implements ResizeHandler {

		@Override
		public void onResize(ResizeEvent event) {
			resizePlaceholder();
		}

	}

}
